package com.easylinkin.linkappapi.grid.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.common.exceptions.BusinessException;
import com.easylinkin.linkappapi.common.exceptions.ExceptionEnum;
import com.easylinkin.linkappapi.common.model.RequestModel;
import com.easylinkin.linkappapi.common.service.OSSServiceImpl;
import com.easylinkin.linkappapi.grid.entity.GridInfo;
import com.easylinkin.linkappapi.grid.entity.GridQrCode;
import com.easylinkin.linkappapi.grid.entity.GridQrCodeRecord;
import com.easylinkin.linkappapi.grid.mapper.GridInfoMapper;
import com.easylinkin.linkappapi.grid.mapper.GridQrCodeMapper;
import com.easylinkin.linkappapi.grid.mapper.GridQrCodeRecordMapper;
import com.easylinkin.linkappapi.grid.models.dto.GridQrCodeDTO;
import com.easylinkin.linkappapi.grid.models.dto.GridQrCodeRelationDTO;
import com.easylinkin.linkappapi.grid.models.query.GridQrCodeQuery;
import com.easylinkin.linkappapi.grid.models.query.GridQrCodeRecordQuery;
import com.easylinkin.linkappapi.grid.models.vo.GridQrCodeVO;
import com.easylinkin.linkappapi.grid.service.IGridQrCodeService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.security.entity.LinkappUser;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import site.morn.rest.RestBuilders;
import site.morn.rest.RestMessage;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 网格二维码管理 服务实现类
 *
 * @author
 * @since 2024-05-27
 */
@Service
public class GridQrCodeServiceImpl extends ServiceImpl<GridQrCodeMapper, GridQrCode> implements IGridQrCodeService {

    public static final String GRID_CODE = "GRID";
    @Autowired
    private LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    private OSSServiceImpl ossService;
    @Resource
    private GridQrCodeRecordMapper gridQrCodeRecordMapper;
    @Resource
    private GridInfoMapper gridInfoMapper;

    @Override
    public RestMessage queryPageList(RequestModel<GridQrCodeQuery> requestModel) {
        GridQrCodeQuery customQueryParams = requestModel.getCustomQueryParams();
        LinkappUser linkappUser = linkappUserContextProducer.getCurrent();
        IPage pageList = this.page(requestModel.getPage(), Wrappers.<GridQrCode>lambdaQuery()
                .like(StrUtil.isNotBlank(customQueryParams.getCode()), GridQrCode::getCode, customQueryParams.getCode())
                .eq(ObjectUtil.isNotNull(customQueryParams.getCodeStatus()), GridQrCode::getCodeStatus, customQueryParams.getCodeStatus())
                .eq(GridQrCode::getTenantId, linkappUser.getTenantId())
                .orderByDesc(GridQrCode::getCreateTime)
        );
        if (CollectionUtil.isNotEmpty(pageList.getRecords())) {
            List<GridQrCodeVO> gridQrCodeVOS = BeanUtil.copyToList(pageList.getRecords(), GridQrCodeVO.class);
            return RestBuilders.successBuilder().data(pageList.setRecords(gridQrCodeVOS)).build();
        }
        return RestBuilders.successBuilder().data(pageList).build();

    }

    @Override
    public String createCode() {
        String inspectionPointCode = this.baseMapper.selectMaxCode();
        if (inspectionPointCode == null) {
            return GRID_CODE + "00001";
        }
        Pattern pattern = Pattern.compile("\\d+(\\.\\d+)?");
        Matcher matcher = pattern.matcher(inspectionPointCode);
        int number = 0;
        while (matcher.find()) {
            number = Integer.parseInt(matcher.group()) + 1;
        }
        int length = 5; // 指定字符串的长度
        return GRID_CODE + String.format("%0" + length + "d", number);
    }

    @Override
    public RestMessage add(GridQrCodeDTO gridQrCodeDTO) {
        int count = this.count(Wrappers.<GridQrCode>lambdaQuery().eq(GridQrCode::getCode, gridQrCodeDTO.getCode()));
        if (count > 0) {
            return RestBuilders.errorBuilder().message("二维码编码已存在").build();
        }
        LinkappUser linkappUser = linkappUserContextProducer.getCurrent();
        GridQrCode gridQrCode = new GridQrCode();
        gridQrCode.setCode(gridQrCodeDTO.getCode());
        gridQrCode.setTenantId(linkappUser.getTenantId());
        gridQrCode.setCodeStatus(0);
        gridQrCode.setCreator(linkappUser.getCreator());
        gridQrCode.setCreateTime(LocalDateTime.now());
        //生成二维码
        try {
            ByteArrayInputStream inputStream = this.generateQRCode(gridQrCodeDTO.getCode());
            String url = ossService.uploadImg(inputStream, gridQrCodeDTO.getCode(), null);
            gridQrCode.setImageUrl(url);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        boolean save = this.save(gridQrCode);
        if (save) {
            GridQrCodeRecord gridQrCodeRecord = new GridQrCodeRecord();
            gridQrCodeRecord.setCode(gridQrCodeDTO.getCode());
            gridQrCodeRecord.setStatus(0);
            gridQrCodeRecord.setStartTime(LocalDateTime.now());
            this.gridQrCodeRecordMapper.insert(gridQrCodeRecord);
            return RestBuilders.successBuilder().message("新增成功").build();
        }
        return RestBuilders.errorBuilder().message("新增失败").build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RestMessage relevance(GridQrCodeRelationDTO gridQrCodeRelationDTO) {
        GridQrCode gridQrCode = this.getById(gridQrCodeRelationDTO.getId());
        if (ObjectUtil.isNull(gridQrCode)) {
            return RestBuilders.errorBuilder().message("二维码不存在").build();
        }
        if(0!=gridQrCode.getCodeStatus()){
            return RestBuilders.errorBuilder().message("二维码非空闲状态，无法关联").build();
        }
        GridInfo gridInfo = this.gridInfoMapper.selectById(gridQrCodeRelationDTO.getGridId());
        if (ObjectUtil.isNull(gridInfo)) {
            return RestBuilders.errorBuilder().message("网格不存在").build();
        }
        //查询二维码是否在使用中
        GridQrCodeRecord gridQrCodeRecord = this.gridQrCodeRecordMapper.selectOne(Wrappers.<GridQrCodeRecord>lambdaQuery()
                .eq(GridQrCodeRecord::getStatus, 1)
                .eq(GridQrCodeRecord::getCode, gridQrCode.getCode())
        );
        if (ObjectUtil.isNotNull(gridQrCodeRecord)) {
            // 使用中，将二维码记录状态更新
            gridQrCodeRecord.setStatus(2);
            gridQrCodeRecord.setEndTime(LocalDateTime.now());
            this.gridInfoMapper.uptadeQrToNull(gridInfo.getId());
            this.gridQrCodeRecordMapper.updateById(gridQrCodeRecord);
        }
        if(StrUtil.isNotBlank(gridInfo.getQrCode())&&!gridInfo.getQrCode().equals(gridQrCode.getCode())){
            GridQrCode gridQrCodeOld = this.getOne(Wrappers.<GridQrCode>lambdaQuery()
                    .eq(GridQrCode::getCode, gridInfo.getQrCode())
                    .last("limit 1")
            );
            this.update(Wrappers.<GridQrCode>lambdaUpdate()
                    .set(GridQrCode::getCodeStatus, 0)
                    .set(GridQrCode::getGridName, "")
                    .eq(GridQrCode::getId, gridQrCodeOld.getId())
            );
            GridQrCodeRecord gridQrCodeRecord2 = this.gridQrCodeRecordMapper.selectOne(Wrappers.<GridQrCodeRecord>lambdaQuery()
                    .eq(GridQrCodeRecord::getStatus, 1)
                    .eq(GridQrCodeRecord::getCode, gridQrCodeOld.getCode())
            );
            if(ObjectUtil.isNotNull(gridQrCodeRecord2)){
                GridQrCodeRecord qrCodeRecord = new GridQrCodeRecord();
                qrCodeRecord.setId(gridQrCodeRecord2.getId());
                qrCodeRecord.setStatus(2);
                qrCodeRecord.setEndTime(LocalDateTime.now());
                gridQrCodeRecordMapper.updateById(qrCodeRecord);
            }
        }
        // 新增二维码使用记录
        GridQrCodeRecord qrCodeRecord = new GridQrCodeRecord();
        qrCodeRecord.setCode(gridQrCode.getCode());
        qrCodeRecord.setStatus(1);
        qrCodeRecord.setStartTime(LocalDateTime.now());
        qrCodeRecord.setGridId(gridInfo.getId().toString());
        qrCodeRecord.setGridName(gridInfo.getGridName());
        int insert = this.gridQrCodeRecordMapper.insert(qrCodeRecord);
        if (insert <= 0) {
            throw new RuntimeException();
        }
        //更新网格关联信息
        GridInfo gridInfo1 = new GridInfo();
        gridInfo1.setId(gridInfo.getId());
        gridInfo1.setQrCode(gridQrCode.getCode());
        int i = this.gridInfoMapper.updateById(gridInfo1);
        if (i <= 0) {
            throw new RuntimeException();
        }
        // 更新二维码关联信息
        GridQrCode gridQrCode1 = new GridQrCode();
        gridQrCode1.setId(gridQrCode.getId());
        gridQrCode1.setGridId(gridInfo.getId().toString());
        gridQrCode1.setGridName(gridInfo.getGridName());
        gridQrCode1.setCodeStatus(1);
        boolean b = this.updateById(gridQrCode1);
        if (b) {
            return RestBuilders.successBuilder().message("关联成功").build();
        }
        return RestBuilders.errorBuilder().message("关联失败").build();
    }

    @Override
    public RestMessage disassociate(String id) {
        GridQrCode gridQrCode = this.getById(id);
        if (ObjectUtil.isNull(gridQrCode)) {
            return RestBuilders.errorBuilder().message("二维码不存在").build();
        }

        //查询二维码是否在使用中
        GridQrCodeRecord gridQrCodeRecord = this.gridQrCodeRecordMapper.selectOne(Wrappers.<GridQrCodeRecord>lambdaQuery()
                .eq(GridQrCodeRecord::getStatus, 1)
                .eq(GridQrCodeRecord::getCode, gridQrCode.getCode())
        );
        this.gridInfoMapper.uptadeQrToNull(Integer.valueOf(gridQrCode.getGridId()));
        if (ObjectUtil.isNotNull(gridQrCodeRecord)) {
            // 使用中，将二维码记录状态更新
            gridQrCodeRecord.setStatus(2);
            gridQrCodeRecord.setEndTime(LocalDateTime.now());
            this.gridQrCodeRecordMapper.updateById(gridQrCodeRecord);
        }
        this.baseMapper.clearGrid(gridQrCode.getId());
        return RestBuilders.successBuilder().message("取消关联成功").build();
    }

    @Override
    public RestMessage getUseList(RequestModel<GridQrCodeRecordQuery> requestModel) {
        GridQrCodeRecordQuery gridQrCodeRecordQuery = requestModel.getCustomQueryParams();
        Assert.notNull(gridQrCodeRecordQuery.getCode(), "code不能为空");
        IPage<GridQrCodeRecord> page = this.gridQrCodeRecordMapper.selectPage(requestModel.getPage(),
                Wrappers.<GridQrCodeRecord>lambdaQuery()
                .eq(GridQrCodeRecord::getCode, gridQrCodeRecordQuery.getCode())
                .like(StrUtil.isNotBlank(gridQrCodeRecordQuery.getKeyword()), GridQrCodeRecord::getGridName, gridQrCodeRecordQuery.getKeyword())
                .orderByDesc(GridQrCodeRecord::getStartTime)
        );
        return RestBuilders.successBuilder().data(page).build();
    }

    @Override
    public RestMessage querySelectList() {
        LinkappUser linkappUser = linkappUserContextProducer.getCurrent();
        List<GridQrCode> list = this.list(Wrappers.<GridQrCode>lambdaQuery().eq(GridQrCode::getTenantId, linkappUser.getTenantId()));
        return RestBuilders.successBuilder().data(list).build();
    }

    @Override
    public GridQrCodeVO getByCode(String code) {
        if (StrUtil.isBlank(code)) {
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.DATA_CHECK_ERROR, "二维码CODE不能为空");
        }

        GridQrCodeVO gridQrCodeVO = baseMapper.selectOneByCode(code);
        if (ObjectUtil.isNull(gridQrCodeVO)) {
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.RECORD_NOT_EXISTS_ERROR, "二维码不存在");
        }

        // 状态 0:空闲 1:在用 2:异常 3:作废
        Set<Integer> useStatus = CollUtil.newHashSet(0, 1);
        if (!useStatus.contains(gridQrCodeVO.getCodeStatus())) {
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.DATA_CHECK_ERROR, "二维码已失效，请联系管理员");
        }

        if (StrUtil.isBlank(gridQrCodeVO.getGridId())) {
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.DATA_CHECK_ERROR, "当前二维码未绑定网格，请联系管理员配置");
        }

        return gridQrCodeVO;
    }

    public ByteArrayInputStream generateQRCode(String data) {
        int width = 300;
        int height = 300;
        ByteArrayInputStream inputStream = null;
        try {
            Map<EncodeHintType, Object> hints = new HashMap<>();
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 设置字符编码
            hints.put(EncodeHintType.ERROR_CORRECTION, com.google.zxing.qrcode.decoder.ErrorCorrectionLevel.H); // 错误纠正级别
            hints.put(EncodeHintType.MARGIN, 1); // 边距
            MultiFormatWriter writer = new MultiFormatWriter();
            BitMatrix bitMatrix = writer.encode(data, BarcodeFormat.QR_CODE, width, height, hints);
            // 创建BufferedImage对象来表示QR码
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    image.setRGB(x, y, bitMatrix.get(x, y) ? Color.BLACK.getRGB() : Color.WHITE.getRGB());
                }
            }
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(image, "png", outputStream);
            byte[] byteArray = outputStream.toByteArray();
            inputStream = new ByteArrayInputStream(byteArray);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return inputStream;
    }
}